<template>
  <div id="webgl"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as THREE from "three";
import earth_img from "../assets/PerspectiveCamera.png";
import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";

// 创建3D场景对象Scene
const scene = new THREE.Scene();
//创建一个长方体几何对象Geometry
const geometry = new THREE.PlaneGeometry(0, 0); //长方体
//纹理贴图加载器TextureLoader
const texLoader = new THREE.TextureLoader();
// .load()方法加载图像，返回一个纹理对象Texture
const texture = texLoader.load(earth_img);
// 设置阵列模式
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
// uv两个方向纹理重复数量
texture.repeat.set(2, 2); //注意选择合适的阵列数量
//创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
  transparent: true,
  // color: 0x00ffff,
  // 设置纹理贴图：Texture对象作为材质map属性的属性值
  map: texture, // map表示材质的颜色贴图属性
  side: THREE.DoubleSide, // 两面都能看到
});
const vertices = new Float32Array([
  // 下面索引值对应顶点位置数据中的顶点坐标
  0, 0, 0, 100, 0, 0, 100, 100, 0, 0, 100, 0,
]);
geometry.attributes.position = new THREE.BufferAttribute(vertices, 3); //3个为一组
// Uint16Array类型数组创建顶点索引数据
// 顶点是有顺序的，逆时针为正面，顺时针为反面
const indexes = new Uint16Array([
  // 下面索引值对应顶点位置数据中的顶点坐标，绘制点的顺序，有两个点共用顶点
  0, 1, 2, 0, 2, 3,
]);
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组
// 设置2个顶点组，形成2个材质
geometry.addGroup(0, 3, 0); // 从第0个顶点开始，绘制3个顶点，材质索引为0
geometry.addGroup(3, 3, 1); // 从第3个顶点开始，绘制3个顶点，材质索引为1
const material1 = new THREE.MeshBasicMaterial({
  color: 0x00ff00,
  side: THREE.DoubleSide,
});
const material2 = new THREE.MeshBasicMaterial({
  color: 0xff0000,
  side: THREE.DoubleSide,
});

// 矩形平面，有索引，两个三角形，有2个顶点重合，有4个顶点
// 每个顶点的法线数据和顶点位置数据一一对应
// 法线的作用是计算光如何反射
const normals = new Float32Array([
  0,
  0,
  1, //顶点1法线( 法向量 )
  0,
  0,
  1, //顶点2法线
  0,
  0,
  1, //顶点3法线
  0,
  0,
  1, //顶点4法线
]);
// 设置几何体的顶点法线属性.attributes.normal
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3);
// 计算出法向量，反射
geometry.computeVertexNormals();

// 移动顶点
geometry.translate(-50, 0, 0);
geometry.rotateX(Math.PI / 2);
geometry.scale(2, 2, 2);

/**纹理坐标0~1之间随意定义*/
// 默认值 0, 1, 1, 1, 0, 0, 1, 0，uv坐标和顶点坐标是一一对应的
// uv坐标是纹理坐标，顶点坐标是几何对象坐标，uv坐标和顶点坐标对应关系，决定了纹理如何贴图
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
// 设置几何体attributes属性的位置normal属性
geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2); //2个为一组,表示一个顶点的纹理坐标
// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, [material1, material2]); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标，默认是坐标原点
mesh.position.set(0, 0, 0);

// 创建法向量辅助器
const helper = new VertexNormalsHelper(mesh, 50, 0xff0000);
scene.add(helper);

scene.add(mesh);

// 定义threejs输出画布的尺寸(单位:像素px)
const width = window.innerWidth; //宽度
const height = window.innerHeight; //高度
// 实例化一个透视投影相机对象
const camera = new THREE.OrthographicCamera(
  -100 * (width / height),
  100 * (width / height),
  100,
  -100,
  1,
  3000
);
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(0, 0, 100);
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(mesh.position); //指向mesh对应的位置

// AxesHelper：辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

const pointLight = new THREE.PointLight(0xffffff, 1.0);
pointLight.decay = 0.0;
pointLight.position.set(0, 0, 3000);
scene.add(pointLight);
const pointLight2 = new THREE.PointLight(0xffffff, 1.0);
pointLight2.decay = 0.0;
pointLight2.position.set(3000, 0, 0);
scene.add(pointLight2);
const pointLight3 = new THREE.PointLight(0xffffff, 1.0);
pointLight3.decay = 0.0;
pointLight3.position.set(0, 0, -3000);
scene.add(pointLight3);
const pointLight4 = new THREE.PointLight(0xffffff, 1.0);
pointLight4.decay = 0.0;
pointLight4.position.set(-3000, 0, 0);
scene.add(pointLight4);

// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();

renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
renderer.setClearColor(0xffffff, 1);
renderer.render(scene, camera);

// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数，重新调用渲染器渲染三维场景
controls.addEventListener("change", function () {
  renderer.render(scene, camera); //执行渲染操作
}); //监听鼠标、键盘事件

function render() {
  texture.offset.x += 0.001;
  renderer.render(scene, camera); //执行渲染操作
  requestAnimationFrame(render);
}
render();

onMounted(() => {
  document.getElementById("webgl").appendChild(renderer.domElement);
});
</script>
